import { codeSettings } from "@renderer/config/code-settings";
import { useTheme } from "@renderer/context/theme-provider";
import { codeToHtmlCached } from "@renderer/lib/shiki-highlighter";
import {
  type HighlightChunkResult,
  type ShikiPreProperties,
  shikiStreamService,
} from "@renderer/services/shiki-stream-service";
import type React from "react";
import {
  createContext,
  type PropsWithChildren,
  use,
  useCallback,
  useEffect,
  useMemo,
} from "react";

interface CodeStyleContextType {
  highlightCodeChunk: (
    trunk: string,
    language: string,
    callerId: string,
  ) => Promise<HighlightChunkResult>;
  highlightStreamingCode: (
    code: string,
    language: string,
    callerId: string,
  ) => Promise<HighlightChunkResult>;
  cleanupTokenizers: (callerId: string) => void;
  getShikiPreProperties: (language: string) => Promise<ShikiPreProperties>;
  highlightCode: (code: string, language: string) => Promise<string>;
  themeNames: string[];
  activeShikiTheme: string;
  isShikiThemeDark: boolean;
  languageMap: Record<string, string>;
}

const defaultCodeStyleContext: CodeStyleContextType = {
  highlightCodeChunk: async () => ({ lines: [], recall: 0 }),
  highlightStreamingCode: async () => ({ lines: [], recall: 0 }),
  cleanupTokenizers: () => {},
  getShikiPreProperties: async () => ({ class: "", style: "", tabindex: 0 }),
  highlightCode: async () => "",
  themeNames: ["auto"],
  activeShikiTheme: "auto",
  isShikiThemeDark: false,
  languageMap: {},
};

const CodeStyleContext = createContext<CodeStyleContextType>(
  defaultCodeStyleContext,
);

export const CodeStyleProvider: React.FC<PropsWithChildren> = ({
  children,
}) => {
  const { codeViewer } = codeSettings;
  const { theme, isSystemDark } = useTheme();
  const actualTheme = useMemo(() => {
    return theme === "system" ? (isSystemDark ? "dark" : "light") : theme;
  }, [theme, isSystemDark]);

  const themeNames = useMemo(() => {
    return ["vitesse-light", "vitesse-dark"];
  }, []);

  const activeShikiTheme = useMemo(() => {
    const field = actualTheme === "light" ? "themeLight" : "themeDark";
    const codeStyle = codeViewer[field];
    return !codeStyle || codeStyle === "auto" || !themeNames.includes(codeStyle)
      ? actualTheme === "light"
        ? "vitesse-light"
        : "vitesse-dark"
      : codeStyle;
  }, [actualTheme, codeViewer, themeNames]);

  const isShikiThemeDark = useMemo(() => {
    return (
      activeShikiTheme.includes("dark") || activeShikiTheme.includes("Dark")
    );
  }, [activeShikiTheme]);

  const languageMap = useMemo(() => {
    return {
      bash: "shell",
      "objective-c++": "objective-cpp",
      svg: "xml",
      vab: "vb",
      graphviz: "dot",
    } as Record<string, string>;
  }, []);

  useEffect(() => {
    return () => {
      shikiStreamService.dispose();
    };
  }, []);

  const highlightCodeChunk = useCallback(
    async (trunk: string, language: string, callerId: string) => {
      const normalizedLang =
        languageMap[language as keyof typeof languageMap] ||
        language.toLowerCase();
      return shikiStreamService.highlightCodeChunk(
        trunk,
        normalizedLang,
        activeShikiTheme,
        callerId,
      );
    },
    [activeShikiTheme, languageMap],
  );

  const cleanupTokenizers = useCallback((callerId: string) => {
    shikiStreamService.cleanupTokenizers(callerId);
  }, []);

  const highlightStreamingCode = useCallback(
    async (fullContent: string, language: string, callerId: string) => {
      const normalizedLang =
        languageMap[language as keyof typeof languageMap] ||
        language.toLowerCase();
      return shikiStreamService.highlightStreamingCode(
        fullContent,
        normalizedLang,
        activeShikiTheme,
        callerId,
      );
    },
    [activeShikiTheme, languageMap],
  );

  const getShikiPreProperties = useCallback(
    async (language: string) => {
      const normalizedLang =
        languageMap[language as keyof typeof languageMap] ||
        language.toLowerCase();
      return shikiStreamService.getShikiPreProperties(
        normalizedLang,
        activeShikiTheme,
      );
    },
    [activeShikiTheme, languageMap],
  );

  const highlightCode = useCallback(
    async (code: string, language: string) => {
      try {
        const themeMode = isShikiThemeDark ? "dark" : "light";
        return await codeToHtmlCached(code, language, themeMode);
      } catch (_error) {
        return `<pre><code>${code}</code></pre>`;
      }
    },
    [isShikiThemeDark],
  );

  const contextValue = useMemo(
    () => ({
      highlightCodeChunk,
      highlightStreamingCode,
      cleanupTokenizers,
      getShikiPreProperties,
      highlightCode,
      themeNames,
      activeShikiTheme,
      isShikiThemeDark,
      languageMap,
    }),
    [
      highlightCodeChunk,
      highlightStreamingCode,
      cleanupTokenizers,
      getShikiPreProperties,
      highlightCode,
      themeNames,
      activeShikiTheme,
      isShikiThemeDark,
      languageMap,
    ],
  );

  return <CodeStyleContext value={contextValue}>{children}</CodeStyleContext>;
};

export const useCodeStyle = () => {
  const context = use(CodeStyleContext);
  if (!context) {
    throw new Error("useCodeStyle must be used within a CodeStyleProvider");
  }
  return context;
};
